package io.renren.modules.health.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.renren.common.constant.Constant;
import io.renren.common.exception.RenException;
import io.renren.common.page.PageData;
import io.renren.common.service.impl.CrudServiceImpl;
import io.renren.modules.health.dao.FoodPlanDao;
import io.renren.modules.health.dto.*;
import io.renren.modules.health.entity.FoodPlanDetailEntity;
import io.renren.modules.health.entity.FoodPlanEntity;
import io.renren.modules.health.entity.HmCustomerEntity;
import io.renren.modules.health.entity.HmQuestionnaireEntity;
import io.renren.modules.health.service.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;

/**
 * 饮食方案
 *
 * @author Mark
 * @since 1.0.0 2024-03-24
 */
@Service
public class FoodPlanServiceImpl extends CrudServiceImpl<FoodPlanDao, FoodPlanEntity, FoodPlanDTO> implements FoodPlanService {

    @Autowired
    private FoodService foodService;
    @Autowired
    private HmCustomerService hmCustomerService;
    @Autowired
    private HmWeightRecordService hmWeightRecordService;
    @Autowired
    private FoodPrePlanService foodPrePlanService;
    @Autowired
    private FoodPrePlanDetailService foodPrePlanDetailService;
    @Autowired
    private HmQuestionnaireServiceImpl hmQuestionnaireService;
    @Autowired
    private FoodPlanDetailService foodPlanDetailService;
    @Autowired
    private FoodPlanDao foodPlanDao;
    /**
     * 制定饮食计划
     */
    @Transactional
    @Override
    public synchronized List<FoodPlanDTO> genCustomerFoodPlan(Long cusId, boolean deleteAfterPlan) {

        //调查问卷填写完需要重新生成计划
        if (deleteAfterPlan) {
            foodPlanDao.delete(new LambdaQueryWrapper<FoodPlanEntity>().eq(FoodPlanEntity::getCustomId,cusId).gt(FoodPlanEntity::getPlanDate,LocalDate.now()));
        }

        // 校验是否有今日计划，没有生成
        if (!deleteAfterPlan && validCurrDayHavePlan(cusId)) return new ArrayList<>();

        List<FoodPlanDTO> foodPlanDTOList = new ArrayList<>(10);
        try {
            // 查询客户信息
            HmCustomerEntity customerEntity = hmCustomerService.selectById(cusId);
            double bmi = calculateBmi(cusId, customerEntity);
            // 查询是否做过问卷
            List<HmQuestionnaireEntity> questionnaireEntities = hmQuestionnaireService.getNewestByCusId(cusId);
            if (questionnaireEntities == null || questionnaireEntities.isEmpty()) {
                genFoodPlanNoQuestion(cusId, bmi, foodPlanDTOList);
            } else {
                genFoodPlanHaveQuestion(cusId, bmi, foodPlanDTOList);
            }
            this.insertFoodPlanBatch(foodPlanDTOList);
        } catch (RenException e) {
            log.error("饮食计划推荐RenException异常",e);
            throw e;
        } catch (Exception e) {
            log.error("饮食计划推荐异常",e);
            throw new RenException(10034,"饮食计划推荐异常");
        }
        return foodPlanDTOList;
    }

    /**
     * 校验是否有今日计划，没有生成
     * @param cusId
     * @return
     */
    private boolean validCurrDayHavePlan(Long cusId) {
        Long planCount = foodPlanDao.selectCount(new QueryWrapper<FoodPlanEntity>()
                .eq("custom_id", cusId)
                .ge("plan_date",LocalDate.now())
        );
        return planCount > 0;
    }

    /**
     * 做过问卷调查，生成7天
     * @param cusId
     * @param bmi
     * @param foodPlanDTOList
     */
    private void genFoodPlanHaveQuestion(Long cusId, double bmi, List<FoodPlanDTO> foodPlanDTOList) {
//        HashMap<String, Map<String,String>> bmiFoodScope = new HashMap<>();
//        HashMap<String, String> genderQuan = new HashMap<>();
//        genderQuan.put("M","2400-3000");
//        genderQuan.put("W","1800-2400");
//        bmiFoodScope.put("18.5-24.9",genderQuan);
//        genderQuan = new HashMap<>();
//        genderQuan.put("M","3200-3600");
//        genderQuan.put("W","2400-3000");
//        bmiFoodScope.put("0-18.5",genderQuan);
//        genderQuan = new HashMap<>();
//        genderQuan.put("M","1600-2200");
//        genderQuan.put("W","800-1600");
//        bmiFoodScope.put("25-100",genderQuan);

        Map<String,Object> paramsMap = new HashMap<>(16);
        paramsMap.put("bmi", bmi);
        List<FoodDTO> foodDTOS = foodService.list(paramsMap);
        if (foodDTOS == null || foodDTOS.isEmpty())
            throw new RenException(10036,"未查询到符合条件食品");

        paramsMap = new HashMap<>();
        paramsMap.put("cusId",cusId);
        paramsMap.put("orderFiled","create_date");
//        查询用户的问卷调查列表
        List<HmQuestionnaireDTO> hmQuestionnaireDTOS = hmQuestionnaireService.list(paramsMap);
        if (hmQuestionnaireDTOS != null && !hmQuestionnaireDTOS.isEmpty()) {
            //根据患者疾病过滤食品
            // 如果食品的不良疾病列表中包含问卷中的任何一个疾病ID，则移除该食品
            Set<String> illIds = new HashSet<>(Arrays.asList(hmQuestionnaireDTOS.get(0).getIllIds().split(",")));
            foodDTOS.removeIf(foodDTO ->
                    Stream.of(foodDTO.getBadIllness().split(",")).anyMatch(illIds::contains)
            );
        }

        // 第一层日期,生成7天
        LongStream.range(0,7).forEach(day -> {
            //第二层时段,每天早\中\晚
            IntStream.range(1, 4).forEach(planDayTime -> {
                FoodPlanDTO foodPlanDTO = new FoodPlanDTO();
                foodPlanDTO.setCustomId(cusId);
                foodPlanDTO.setFoodStatus(1);
                foodPlanDTO.setPlanDayTime(planDayTime);
                foodPlanDTO.setPlanDate(Date.from(LocalDate.now().plusDays(day).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));

                List<FoodDTO> filterFoodDTOS = foodDTOS.stream()
                        .filter(foodDTO -> foodDTO.getPlanDayTime().contains(String.valueOf(planDayTime)))
                        .collect(Collectors.toList());
                if (filterFoodDTOS.isEmpty()) return;

                // 使用流操作进行分组
                Map<String, List<FoodDTO>> planDayTimeFoodGroup = filterFoodDTOS.stream()
                        .flatMap(foodDTO -> Arrays.stream(foodDTO.getPlanDayTime().split(",")) // 将逗号分隔的枚举值拆分成流
                                .map(String::trim) // 去除枚举值的空格
                                .map(pdt -> new AbstractMap.SimpleEntry<>(pdt, foodDTO))) // 构建键值对，键为枚举值，值为FoodDTO对象
                        .collect(Collectors.groupingBy(Map.Entry::getKey, // 按照枚举值分组
                                Collectors.mapping(Map.Entry::getValue, Collectors.toList()))); // 将FoodDTO对象收集到列表中

                if (planDayTimeFoodGroup.get(String.valueOf(planDayTime)).isEmpty()) return;
                // 早、晚一个饮品一个主食，中一个主食，不需要饮品
                // foodType:1-主食 2-饮品 3-其他
                if (planDayTime == 1) {
                    createFoodAddToPlan(planDayTimeFoodGroup.get(String.valueOf(planDayTime)), foodPlanDTO, 2L);
                    for (int i = 0; i < 2; i++) {
                        createFoodAddToPlan(planDayTimeFoodGroup.get(String.valueOf(planDayTime)), foodPlanDTO, 3L);
                    }
                }
                if (planDayTime == 2) {
                    createFoodAddToPlan(planDayTimeFoodGroup.get(String.valueOf(planDayTime)), foodPlanDTO, 1L);
                    for (int i = 0; i < 3; i++) {
                        createFoodAddToPlan(planDayTimeFoodGroup.get(String.valueOf(planDayTime)), foodPlanDTO,3L);
                    }
                }
                if (planDayTime == 3) {
                    createFoodAddToPlan(planDayTimeFoodGroup.get(String.valueOf(planDayTime)), foodPlanDTO, 1L);
                    for (int i = 0; i < 2; i++) {
                        createFoodAddToPlan(planDayTimeFoodGroup.get(String.valueOf(planDayTime)), foodPlanDTO,3L);
                    }
                }
                foodPlanDTOList.add(foodPlanDTO);
            });
        });
    }

    /**
     * 将筛选后的食物列表添加到饮食计划中
     *
     * @param filterFoodDTOS 筛选后的食物列表
     * @param foodPlanDTO 饮食计划DTO
     */
    private static void createFoodAddToPlan(List<FoodDTO> filterFoodDTOS, FoodPlanDTO foodPlanDTO,Long foodType) {
        Random random = new Random();
        FoodDTO foodDTO;

        List<FoodDTO> finalFilterFoodList = filterFoodDTOS.stream()
                .filter(foodDTO1 -> foodDTO1.getFoodType().equals(foodType))
                .collect(Collectors.toList());
        if (finalFilterFoodList.isEmpty())
            throw new RenException(10036,"未查询到符合条件食品");
        int i = 0;
        while (true) {
            if (++i > 100) return;
            foodDTO = finalFilterFoodList.get(random.nextInt(finalFilterFoodList.size()));
            FoodDTO finalFoodDTO = foodDTO;
            if (foodPlanDTO.getFoodPlanDetails().stream().anyMatch(foodDTO1 -> Objects.equals(finalFoodDTO.getId(), foodDTO1.getFoodId()))){
                continue;
            }
            break;
        }

        FoodPlanDetailDTO foodPlanDetailDTO = new FoodPlanDetailDTO();
        foodPlanDetailDTO.setFoodId(foodDTO.getId())
                .setFoodName(foodDTO.getFoodName())
                .setFoodWeight(foodDTO.getWeight())
                .setTrophy(foodDTO.getFoodTrophy())
                .setDeleted(0);
        foodPlanDTO.getFoodPlanDetails().add(foodPlanDetailDTO);
    }

    /**
     * 没有做过问卷调查，生成1天,每天早\中\晚
     * @param cusId 用户ID
     * @param bmi 用户BMI值
     * @param foodPlanDTOList 饮食计划列表
     */
    private void genFoodPlanNoQuestion(Long cusId, double bmi, List<FoodPlanDTO> foodPlanDTOList) {
        Random rand = new Random();
        Map<String,Object> paramsMap = new HashMap<>(16);
        paramsMap.put("bmi", bmi);
        PageData<FoodPrePlanDTO> prePlanDTOPageData = foodPrePlanService.page(paramsMap);
        if (prePlanDTOPageData.getList() == null || prePlanDTOPageData.getList().isEmpty())
            throw new RenException(10035,"未查询到符合条件自定义方案");
        paramsMap = new HashMap<>(16);
        paramsMap.put("foodPrePlanIds",prePlanDTOPageData.getList().stream().map(FoodPrePlanDTO::getId).collect(Collectors.toList()));
        List<FoodPrePlanDetailDTO> foodPrePlanDetailDTOS = foodPrePlanDetailService.list(paramsMap);
        prePlanDTOPageData.getList().forEach(foodPrePlanDTO -> {
            foodPrePlanDetailDTOS.forEach(foodPrePlanDetailDTO -> {
                if (foodPrePlanDTO.getId().equals(foodPrePlanDetailDTO.getFoodPrePlanId()))
                    foodPrePlanDTO.getFoodPrePlanDetails().add(foodPrePlanDetailDTO);

            });
        });
        // 第一层日期,生成1天
        LongStream.range(0,1).forEach(day -> {
            //第二层时段,每天早\中\晚
            IntStream.range(1,4).forEach(planDayTime -> {
                List<FoodPrePlanDTO> filterFoodPrePlanDTOS = prePlanDTOPageData.getList()
                        .stream()
                        .filter(ppd -> ppd.getPlanDayTime().equals(planDayTime)).collect(Collectors.toList());
                if (filterFoodPrePlanDTOS.isEmpty()) return;
                FoodPrePlanDTO foodPrePlanDTO = filterFoodPrePlanDTOS.get(rand.nextInt(filterFoodPrePlanDTOS.size()));
                FoodPlanDTO foodPlanDTO = createFoodPlanDTO(cusId, day, planDayTime, foodPrePlanDTO);
                foodPlanDTO.setFoodPlanDetails(
                        foodPrePlanDTO.getFoodPrePlanDetails().stream().map(foodPrePlanDetailDTO -> {
                            FoodPlanDetailDTO foodPlanDetailDTO = new FoodPlanDetailDTO();
                            BeanUtils.copyProperties(foodPrePlanDetailDTO,foodPlanDetailDTO);
                            return foodPlanDetailDTO;
                        }).collect(Collectors.toList()));
                foodPlanDTOList.add(foodPlanDTO);
            });
        });
    }

    /**
     * 创建 FoodPlanDTO 对象
     *
     * @param cusId 用户ID
     * @param day 偏移天数
     * @param planDayTime 计划时段
     * @param foodPrePlanDTO 预定义饮食计划DTO
     * @return FoodPlanDTO 对象
     */
    private static FoodPlanDTO createFoodPlanDTO(Long cusId, long day, int planDayTime, FoodPrePlanDTO foodPrePlanDTO) {
        FoodPlanDTO foodPlanDTO = new FoodPlanDTO();
        BeanUtils.copyProperties(foodPrePlanDTO,foodPlanDTO);
        foodPlanDTO.setCustomId(cusId);
        foodPlanDTO.setFoodStatus(1);
        foodPlanDTO.setPlanDate(Date.from(LocalDate.now().plusDays(day).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
        foodPlanDTO.setPlanDayTime(planDayTime);
        return foodPlanDTO;
    }

    /**
     * 获取BMI值
     *
     * @param cusId 客户ID
     * @param customerEntity 客户实体对象
     * @return 返回BMI值
     */
    private  double calculateBmi(Long cusId, HmCustomerEntity customerEntity) {
        // 查询最新体重
        HashMap<String, Object> params = new HashMap<>();
        params.put(Constant.PAGE,"1");
        params.put(Constant.LIMIT,"1");
        params.put("cusId", cusId);
        params.put(Constant.ORDER,Constant.DESC);
        params.put(Constant.ORDER_FIELD,"update_date");
        PageData<HmWeightRecordDTO> page = hmWeightRecordService.page(params);
        return page.getList().get(0).getWeight() / (Double.parseDouble(String.valueOf(customerEntity.getHeight())) / 100);
    }

    /**
     * 批量插入饮食计划
     *
     * @param foodPlanDTOList 饮食计划列表
     */
    @Override
    public void insertFoodPlanBatch(List<FoodPlanDTO> foodPlanDTOList) {
        List<FoodPlanDetailEntity> foodPlanDetailSaveList  = new ArrayList<>();
        foodPlanDTOList.forEach(foodPlanDTO -> {
            foodPlanDTO.setId(null);
            foodPlanDTO.setCreator(null);
            foodPlanDTO.setCreateDate(null);
            foodPlanDTO.setUpdater(null);
            foodPlanDTO.setUpdateDate(null);
            this.save(foodPlanDTO);
            List<FoodPlanDetailEntity> foodPlanDetailEntities = foodPlanDTO.getFoodPlanDetails().stream().map(foodPlanDetailDTO -> {
                FoodPlanDetailEntity foodPlanDetailEntity = new FoodPlanDetailEntity();
                BeanUtils.copyProperties(foodPlanDetailDTO,foodPlanDetailEntity);
                foodPlanDetailEntity.setId(null);
                foodPlanDetailEntity.setFoodPlanId(foodPlanDTO.getId());
                return foodPlanDetailEntity;
            }).collect(Collectors.toList());
            foodPlanDetailSaveList.addAll(foodPlanDetailEntities);
        });
        foodPlanDetailService.insertBatch(foodPlanDetailSaveList);

    }

    /**
     * 查询客户饮食计划详情
     *
     * @param cusId 客户ID
     * @return 返回饮食计划详情列表
     */
    @Override
    public List<FoodPlanDTO> queryFoodPlanWithDetail(Long cusId) {
        HashMap<String, Object> param = new HashMap<>();
        param.put("cusId",String.valueOf(cusId));
        param.put("currentDate",true);
        List<FoodPlanDTO> foodPlanDTOS = this.list(param);
        param = new HashMap<>();
        param.put("foodPlanIds",foodPlanDTOS.stream().map(FoodPlanDTO::getId).collect(Collectors.toList()));
        List<FoodPlanDetailDTO> foodPlanDetailDTOS = foodPlanDetailService.list(param);
        param = new HashMap<>();
        param.put("foodIds",foodPlanDetailDTOS.stream().map(FoodPlanDetailDTO::getFoodId).collect(Collectors.toList()));
        List<FoodDTO> foodDTOS = foodService.list(param);
        foodPlanDetailDTOS.forEach(foodPlanDetailDTO ->  {
            foodDTOS.forEach(foodDTO -> {
                if (foodPlanDetailDTO.getFoodId().equals(foodDTO.getId())) {
                    foodPlanDetailDTO.setFoodName(foodDTO.getFoodName())
                            .setFoodIconUrl(foodDTO.getFoodIconUrl());
                }
            });
        });
        foodPlanDTOS.forEach(foodPlanDTO ->  {
            foodPlanDTO.setFoodPlanDetails(foodPlanDetailDTOS
                    .stream()
                    .filter(detail -> detail.getFoodPlanId().equals(foodPlanDTO.getId()))
                    .collect(Collectors.toList()));
        });
        return foodPlanDTOS;
    }


    @Override
    public QueryWrapper<FoodPlanEntity> getWrapper(Map<String, Object> params){
        String id = (String)params.get("id");
        String cusId = (String)params.get("cusId");
        Boolean currentDate = (Boolean)params.get("currentDate");

        QueryWrapper<FoodPlanEntity> wrapper = new QueryWrapper<>();
        wrapper.eq(StrUtil.isNotBlank(id), "id", id);
        wrapper.eq(StrUtil.isNotBlank(cusId), "custom_id", cusId);
        wrapper.ge(currentDate != null && currentDate, "plan_date", LocalDate.now());

        return wrapper;
    }


}